﻿using System;
using System.Runtime.InteropServices;
using System.ComponentModel.Design;
using Microsoft.VisualStudio.Shell;
using CodeStash.Addin.Core.Interfaces;
using Microsoft.VisualStudio.Shell.Interop;
using CodeStash.Addin.MEF;
using Cinch;
using CodeStash.Addin.Popups;
using CodeStash.Addin.Views;
using CodeStash.Addin.Core;
using System.Diagnostics;
using CodeStash.Common.Helpers;
using Microsoft.VisualStudio.Settings;
using Microsoft.VisualStudio.Shell.Settings;

namespace CodeStash.Addin
{
    /// <summary>
    /// This is the class that implements the package exposed by this assembly.
    ///
    /// The minimum requirement for a class to be considered a valid package for Visual Studio
    /// is to implement the IVsPackage interface and register itself with the shell.
    /// This package uses the helper classes defined inside the Managed Package Framework (MPF)
    /// to do it: it derives from the Package class that provides the implementation of the 
    /// IVsPackage interface and uses the registration attributes defined in the framework to 
    /// register itself and its components with the shell.
    /// </summary>
    // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is
    // a package.
    [PackageRegistration(UseManagedResourcesOnly = true)]
    // This attribute is used to register the informations needed to show the this package
    // in the Help/About dialog of Visual Studio.
    [InstalledProductRegistration("#110", "#112", "1.0")]
    [ProvideOptionPageAttribute(typeof(CodeStashSettingsPropertyPage), "CodeStash", "Settings", 101, 1000, true)]
    // This attribute is needed to let the shell know that this package exposes some menus.
    [ProvideMenuResource("Menus.ctmenu", 1)]
    [Guid(GuidList.guidCodeStash_AddinPkgString)]
    // Auto load the menus when the solution loads.
    [ProvideAutoLoad("{f1536ef8-92ec-443c-9ed7-fdadf150da82}")]
    public sealed class CodeStash_AddinPackage : Package
    {
        private OleMenuCommand saveSnippetMenu;

        /// <summary>
        /// Default constructor of the package.
        /// Inside this method you can place any initialization code that does not require 
        /// any Visual Studio service because at this point the package object is created but 
        /// not sited yet inside Visual Studio environment. The place to do all the other 
        /// initialization is the Initialize method.
        /// </summary>
        public CodeStash_AddinPackage()
        {
            MEF.MEFPartsResolver.Instance.Configure();

            //Register popup window(s)
            IUIVisualizerService uiVisualizerService = MEFPartsResolver.Instance.Resolve<IUIVisualizerService>();
            uiVisualizerService.Register("SnippetPopup", typeof(SnippetPopup));

            this.DocumentService = MEFPartsResolver.Instance.Resolve<IDocumentService>();
        }

        public ILoginDetails LoginDetailsService { get; set; }

        public IDocumentService DocumentService { get; set; }

        /////////////////////////////////////////////////////////////////////////////
        // Overriden Package Implementation
        #region Package Members

        /// <summary>
        /// Initialization of the package; this method is called right after the package is sited, so this is the place
        /// where you can put all the initilaization code that rely on services provided by VisualStudio.
        /// </summary>
        protected override void Initialize()
        {
            base.Initialize();

            GetDTE();

            // Add our command handlers for menu (commands must exist in the .vsct file)
            OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
            if (null != mcs)
            {
                // Create the command for the menu item.
                CommandID menuCommandID = new CommandID(GuidList.CodeStashGrouping, PkgCmdIDList.SaveSnippetId);
                CommandID searchSnippetCommandID = new CommandID(GuidList.CodeStashGrouping, PkgCmdIDList.InsertSnippetId);
                CommandID gotoWebsiteID = new CommandID(GuidList.CodeStashGrouping, PkgCmdIDList.GotoWebsiteId);

                saveSnippetMenu = new OleMenuCommand(AddSnippetMenuItemCallback, menuCommandID);
                saveSnippetMenu.BeforeQueryStatus += OnQuerySaveSnippet;
                mcs.AddCommand(saveSnippetMenu);

                MenuCommand searchMenuItem = new MenuCommand(SearchSnippetsMenuItemCallback, searchSnippetCommandID);
                mcs.AddCommand(searchMenuItem);

                MenuCommand gotoWebsite = new MenuCommand(GotoWebsiteCallback, gotoWebsiteID);
                mcs.AddCommand(gotoWebsite);
            }

            SettingsManager settingsManager = new ShellSettingsManager(this);
            WritableSettingsStore settingsStore = settingsManager.GetWritableSettingsStore(SettingsScope.UserSettings);

            this.LoginDetailsService = MEFPartsResolver.Instance.Resolve<ILoginDetails>();

            LoginDetailsService.SetStore(settingsStore);

        }

        /// <summary>
        /// Go to the codestash website.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void GotoWebsiteCallback(object sender, EventArgs e)
        {
            Process.Start(ConfigurationSettings.CodeStashWebAddress);
        }

        /// <summary>
        /// Event handler called when we want to query the save snippet command.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnQuerySaveSnippet(object sender, EventArgs e)
        {
            OleMenuCommand cmd = sender as OleMenuCommand;
            // As well as whether or not the text is selected, we also need to find out if the user
            // has logged in. If they haven't, we'll have to do something about that.
            if (DocumentService == null || !DocumentService.TextSelected)
                cmd.Enabled = false;
            cmd.Enabled = DocumentService.TextSelected;
        }

        protected override int QueryClose(out bool canClose)
        {

            // Make sure we remove the app domain.
            int retVal = base.QueryClose(out canClose);
            if (canClose && saveSnippetMenu != null)
            {
                // Unhook the query status option.
                saveSnippetMenu.BeforeQueryStatus -= OnQuerySaveSnippet;
            }
            return retVal;
        }

        #endregion

        private bool RestSettingsSpecified()
        {
            LoginDetails login = LoginDetailsService.Load();
            return login.IsValid;
        }

        private void ShowCodeStashSettings()
        {
            EnvDTE.DTE dte = (EnvDTE.DTE)GetGlobalService(typeof(SDTE));
            dte.ExecuteCommand("Tools.Options", GuidList.PropertyPageGuid);
        }

        /// <summary>
        /// These functions are the callback(s) used to execute a command when the a menu item is clicked.
        /// See the Initialize method to see how the menu item is associated to this function using
        /// the OleMenuCommandService service and the MenuCommand class.
        /// </summary>
        private void AddSnippetMenuItemCallback(object sender, EventArgs e)
        {
            if (!RestSettingsSpecified())
            {
                ShowCodeStashSettings();
                return;
            }

            if (DocumentService == null || !DocumentService.TextSelected)
                return;
            new AddSnippetView().ShowModal();
        }

        private void SearchSnippetsMenuItemCallback(object sender, EventArgs e)
        {
            if (!RestSettingsSpecified())
            {
                ShowCodeStashSettings();
                return;
            }

            if (DocumentService == null)
                return;
            new SearchView().ShowDialog();
        }

        private void GetDTE()
        {
            EnvDTE.DTE provider = (EnvDTE.DTE)GetService(typeof(EnvDTE.DTE));
            MEFPartsResolver.Instance.Resolve<IDocumentService>().SetDTE(provider);

            IVsStatusbar statusBar = (IVsStatusbar)GetService(typeof(SVsStatusbar));
            MEFPartsResolver.Instance.Resolve<IStatusBarService>().SetStatusBar(statusBar);
        }
    }
}